package made.ga;

import java.util.ArrayList;

import made.util.Config;
import made.util.GameStateAnalyzer;
import made.util.GameStateRecorder;
import made.util.GameStateAnalyzer.GameEntity;
import made.util.GameStateAnalyzer.GhostEntity;
import made.util.Logger;
import game.controllers.PacManController;
import game.core.Game;

public class GAPacMan implements PacManController, Config
{
    protected GameStateAnalyzer _analyzer;
    GeneticAlgorithm _ga;
    protected int _moves;
    protected ArrayList<Integer> _fitnessParameters;
    protected int _edibleTimeThreshold;
    protected int _fleeNearestGhostThreshold;
    protected int _attackNearestGhostThreshold;
    protected boolean _completeTrainingSetSaved;

    public GAPacMan()
    {
        _analyzer = new GameStateAnalyzer();
        _fitnessParameters = new ArrayList<Integer>(4);

        for (int i = 0; i < 4; i++)
        {
            _fitnessParameters.add(0);
        }

        init();
    }

    public void init()
    {
        _moves = 0;
        _completeTrainingSetSaved = false;

        if (MPM_MODE == MODE.TRAIN)
        {
            _edibleTimeThreshold = 0;
            _fleeNearestGhostThreshold = 0;
            _attackNearestGhostThreshold = 0;
        } else
        {
            _edibleTimeThreshold = 18;
            _fleeNearestGhostThreshold = 7;
            _attackNearestGhostThreshold = 80;
        }

        for (int i = 0; i < 4; i++)
        {
            _fitnessParameters.set(i, 0);
        }

        GameStateRecorder.getInstance().init();
        GameStateRecorder.getInstance().initFilename("GAPacMan");
    }

    public int getAction(Game game, long timeDue)
    {
        // protected Entity _nearest PowerPill;
        int currentPacManIndex = -1;
        int edibleTime = -1;
        int move = -1;

        _fitnessParameters.set(0, game.getScore());
        _fitnessParameters.set(1, game.getTotalTime());
        _fitnessParameters.set(2, _moves);
        _fitnessParameters.set(3, game.getCurLevel());

        ACTION action = ACTION.UNDEFINED;
        currentPacManIndex = game.getCurPacManLoc();
        GhostEntity _nearestGhost = _analyzer.getNearestGhost(game, currentPacManIndex);
        GameEntity _nearestPill = _analyzer.getNearestPillInc(game, currentPacManIndex);

        if (_nearestGhost.index >= 0)
            edibleTime = game.getEdibleTime(_nearestGhost.id);

        // IO.logLine("Ghost distance: " + _nearestGhost.pathDistance);
        // IO.logLine("Decision thresholds set to: " + _edibleTimeThreshold +
        // ", " + _fleeNearestGhostThreshold + ", " +
        // _attackNearestGhostThreshold);

        if (edibleTime < _edibleTimeThreshold)
        {
            if (_nearestGhost.pathDistance < _fleeNearestGhostThreshold)
            {
                move = game.getNextPacManDir(_nearestGhost.index, false, Game.DM.PATH);
                action = ACTION.FROM_NEAREST_GHOST;

                if (MPM_MODE == MODE.CONFIG && RECORD_STATS)
                    if (GameStateRecorder.getInstance().saveBaselinePacManSample(edibleTime, _nearestPill.pathDistance, _nearestGhost.pathDistance,
                            action, 0))
                        _completeTrainingSetSaved = true;
            } else
            {
                move = game.getNextPacManDir(_nearestPill.index, true, Game.DM.PATH);
                action = ACTION.TO_NEAREST_PILL;

                if (MPM_MODE == MODE.CONFIG && RECORD_STATS)
                    if (GameStateRecorder.getInstance().saveBaselinePacManSample(edibleTime, _nearestPill.pathDistance, _nearestGhost.pathDistance,
                            action, 1))
                        _completeTrainingSetSaved = true;
            }
        } else
        {
            if (_nearestGhost.pathDistance < _attackNearestGhostThreshold)
            {
                move = game.getNextPacManDir(_nearestGhost.index, true, Game.DM.PATH);
                action = ACTION.TO_NEAREST_GHOST;

                if (MPM_MODE == MODE.CONFIG && RECORD_STATS)
                    if (GameStateRecorder.getInstance().saveBaselinePacManSample(edibleTime, _nearestPill.pathDistance, _nearestGhost.pathDistance,
                            action, 2))
                        _completeTrainingSetSaved = true;
            } else
            {
                move = game.getNextPacManDir(_nearestPill.index, true, Game.DM.PATH);
                action = ACTION.TO_NEAREST_PILL;

                if (MPM_MODE == MODE.CONFIG && RECORD_STATS)
                    if (GameStateRecorder.getInstance().saveBaselinePacManSample(edibleTime, _nearestPill.pathDistance, _nearestGhost.pathDistance,
                            action, 3))
                        _completeTrainingSetSaved = true;
            }
        }

        // IO.log(move + "\n");
//        Logger.logLine("" + action);

        _moves++;

        return move;
    }

    public int getFitness()
    {
        float fitness = 1.0f * _fitnessParameters.get(0) + 0.0f * _fitnessParameters.get(1) + 0.0f * _fitnessParameters.get(2) + 0.0f
                * _fitnessParameters.get(3);
        // IO.logLine("Fitness: " + fitness);

        return (int) fitness;
    }

    public int getScore()
    {
        return _fitnessParameters.get(0);
    }

    public void printSampleTypeStats()
    {
        GameStateRecorder.getInstance().printSampleTypeStats();
    }

    public boolean completeTrainingSetSaved()
    {
        return _completeTrainingSetSaved;
    }

    public void setPredicateVariables(ArrayList<Integer> thresholds)
    {
        _edibleTimeThreshold = thresholds.get(0);
        _fleeNearestGhostThreshold = thresholds.get(1);
        _attackNearestGhostThreshold = thresholds.get(2);

        // IO.logLine("Decision thresholds set to: " + _edibleTimeThreshold +
        // ", " + _fleeNearestGhostThreshold + ", " +
        // _attackNearestGhostThreshold);
    }
};